﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ReadeAssetManager
{
    public partial class ucSearchService : UserControl, ISearchControl
    {
        AMSServiceRef.User _user;

        private const string DATE_IS = "Date Is";
        private const string DATE_BEG = "Date Beginning";
        private const string DATE_END = "Date Ending";
        private const string DATE_BET = "Date Between";
        private const string DATE_ANY = "Any Date";

        private const string PRICE_IS = "Price Is";
        private const string PRICE_BEG = "Price Beginning";
        private const string PRICE_END = "Price Ending";
        private const string PRICE_BET = "Price Between";
        private const string PRICE_ANY = "Any Price";

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="CurrentUser">[AMSServiceRef.User] The current application user.</param>
        public ucSearchService(AMSServiceRef.User CurrentUser)
        {
            InitializeComponent();
            _user = CurrentUser;
            LoadDateSearchOptions();
            LoadPriceSearchOptions();
        }

        /// <summary>
        /// BuildAssetList
        /// Creates and returns a List of AMSServiceRef.Asset objects matching the search
        /// criteria.  This method is called indirectly from frmMain via a delegate in the
        /// ucSearch control.
        /// </summary>
        /// <returns>[List<AMSServiceRef.Asset>] List of assets matching the search criteria.</returns>
        public List<AMSServiceRef.Asset> BuildAssetList()
        {
            List<AMSServiceRef.Asset> assets = new List<AMSServiceRef.Asset>();

            if (_user != null)
            {
                foreach (AMSServiceRef.Asset asset in _user.Assets)
                {
                    if (IsAssetMatch(asset))
                        assets.Add(asset);
                }
            }

            return assets;
        }

        /// <summary>
        /// IsAssetMatch
        /// Determines whether the given asset matches the search parameters.
        /// </summary>
        /// <param name="CurrentAsset">[AMSServiceRef.Asset] The asset being checked.</param>
        /// <returns>[bool] True if this asset fits the search parameters, otherwise false.</returns>
        private bool IsAssetMatch(AMSServiceRef.Asset CurrentAsset)
        {
            bool isMatch = false;

            // No search options were selected
            if ((txtTitle.Text.Length + txtCompany.Text.Length + txtComments.Text.Length == 0) &&
                !txtPriceFrom.Enabled && !txtPriceTo.Enabled && !dtpFrom.Enabled && !dtpTo.Enabled &&
                cbUnderWarr.CheckState == CheckState.Indeterminate)
                return true;

            foreach (AMSServiceRef.ServiceData s in CurrentAsset.Services)
            {
                isMatch = true;

                // Title
                if (txtTitle.Text.Length > 0 && !s.Title.Contains(txtTitle.Text))
                    continue;

                // Company
                if (txtCompany.Text.Length > 0 && !s.Company.Contains(txtCompany.Text))
                    continue;

                // Comments
                if (txtComments.Text.Length > 0 && !s.Comments.Contains(txtComments.Text))
                    continue;

                // Is Under Warranty
                if (cbUnderWarr.CheckState != CheckState.Indeterminate && cbUnderWarr.Checked != s.UnderWarranty)
                    continue;

                // Price From/To
                if (txtPriceFrom.Enabled && txtPriceTo.Enabled)
                    isMatch = IsValidPrice(txtPriceFrom, cmbPrice, s.Price, true) &&
                            IsValidPrice(txtPriceTo, cmbPrice, s.Price, false);

                else if (txtPriceFrom.Enabled)
                    isMatch = IsValidPrice(txtPriceFrom, cmbPrice, s.Price, true);

                else if (txtPriceTo.Enabled)
                    isMatch = IsValidPrice(txtPriceTo, cmbPrice, s.Price, false);

                if (!isMatch)
                    continue;

                // Date From/To
                if (dtpFrom.Enabled && dtpTo.Enabled)
                    isMatch = IsValidDate(dtpFrom, cmbDate, s.Date, true) &&
                           IsValidDate(dtpTo, cmbDate, s.Date, false);

                else if (dtpFrom.Enabled)
                    isMatch = IsValidDate(dtpFrom, cmbDate, s.Date, true);

                else if (dtpTo.Enabled)
                    isMatch = IsValidDate(dtpTo, cmbDate, s.Date, false);

                if (isMatch)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// LoadDateSearchOptions
        /// Fills the cmbDate control with the default values.
        /// </summary>
        private void LoadDateSearchOptions()
        {
            cmbDate.Items.Clear();
            cmbDate.Items.Add(DATE_IS);
            cmbDate.Items.Add(DATE_BEG);
            cmbDate.Items.Add(DATE_END);
            cmbDate.Items.Add(DATE_BET);
            cmbDate.Items.Add(DATE_ANY);
            cmbDate.Text = DATE_ANY;
        }

        /// <summary>
        /// LoadPriceSearchOptions
        /// Fills the cmbPrice control with the default values.
        /// </summary>
        private void LoadPriceSearchOptions()
        {
            cmbPrice.Items.Clear();
            cmbPrice.Items.Add(PRICE_IS);
            cmbPrice.Items.Add(PRICE_BEG);
            cmbPrice.Items.Add(PRICE_END);
            cmbPrice.Items.Add(PRICE_BET);
            cmbPrice.Items.Add(PRICE_ANY);
            cmbPrice.Text = PRICE_ANY;
        }

        /// <summary>
        /// cmbDate_TextChanged
        /// Enables/disables the appropriate date fields as the date search options are changed.
        /// </summary>
        /// <param name="sender">[object] The cmbDate control.</param>
        /// <param name="e">[EventArgs] The standard event arguments for a TextChanged event.</param>
        private void cmbDate_TextChanged(object sender, EventArgs e)
        {
            switch (cmbDate.Text)
            {
                case DATE_IS:
                    dtpFrom.Enabled = true;
                    dtpTo.Enabled = false;
                    break;
                case DATE_BEG:
                    dtpFrom.Enabled = true;
                    dtpTo.Enabled = false;
                    break;
                case DATE_END:
                    dtpFrom.Enabled = false;
                    dtpTo.Enabled = true;
                    break;
                case DATE_BET:
                    dtpFrom.Enabled = true;
                    dtpTo.Enabled = true;
                    break;
                case DATE_ANY:
                    dtpFrom.Enabled = false;
                    dtpTo.Enabled = false;
                    break;
                default:
                    dtpFrom.Enabled = false;
                    dtpTo.Enabled = false;
                    break;
            }
        }

        /// <summary>
        /// cmbPrice_TextChanged
        /// Enables/disables the appropriate price fields as the price search options are changed.
        /// </summary>
        /// <param name="sender">[object] The cmbPrice control.</param>
        /// <param name="e">[EventArgs] The standard event arguments for a TextChanged event.</param>
        private void cmbPrice_TextChanged(object sender, EventArgs e)
        {
            switch (cmbPrice.Text)
            {
                case PRICE_IS:
                    txtPriceFrom.Enabled = true;
                    txtPriceTo.Enabled = false;
                    break;
                case PRICE_BEG:
                    txtPriceFrom.Enabled = true;
                    txtPriceTo.Enabled = false;
                    break;
                case PRICE_END:
                    txtPriceFrom.Enabled = false;
                    txtPriceTo.Enabled = true;
                    break;
                case PRICE_BET:
                    txtPriceFrom.Enabled = true;
                    txtPriceTo.Enabled = true;
                    break;
                case PRICE_ANY:
                    txtPriceFrom.Enabled = false;
                    txtPriceTo.Enabled = false;
                    break;
                default:
                    txtPriceFrom.Enabled = false;
                    txtPriceTo.Enabled = false;
                    break;
            }
        }

        /// <summary>
        /// IsValidDate
        /// Determines whether the date given in DateToValidate meets the criteria given
        /// by the value in the Picker control and the selected option in the DateOption
        /// control.
        /// </summary>
        /// <param name="Picker">[DateTimePicker] The control containing the criteria value.</param>
        /// <param name="DateOption">[ComboBox] The control indicating how and whether the date should be read.</param>
        /// <param name="DateToValidate">[DateTime] The AMSServiceRef.Asset date being checked.</param>
        /// <param name="FromDate">[bool] True if checking the from date field, otherwise false.  Required for Date Between.</param>
        /// <returns>[bool] True if the DateToValidate meets the criteria, otherwise false.</returns>
        private bool IsValidDate(DateTimePicker Picker, ComboBox DateOption, DateTime DateToValidate, bool FromDate)
        {
            switch (DateOption.Text)
            {
                case DATE_IS:
                    if (FromDate)
                        return (DateToValidate == Picker.Value);
                    break;
                case DATE_BEG:
                    if (FromDate)
                        return (DateToValidate >= Picker.Value);
                    break;
                case DATE_END:
                    if (!FromDate)
                        return (DateToValidate <= Picker.Value);
                    break;
                case DATE_BET:
                    if (FromDate)
                        return (DateToValidate >= Picker.Value);
                    else
                        return (DateToValidate <= Picker.Value);
                    break;
                case DATE_ANY:
                    return true;
                    break;
                default:
                    // An unexpected option was selected.
                    return false;
                    break;
            }
            return false;
        }

        /// <summary>
        /// IsValidPrice
        /// Determines whether the price given in PriceToValidate meets the criteria given
        /// by the value in the PriceField control and the selected option in the PriceOption
        /// control.
        /// </summary>
        /// <param name="PriceField">[TextBox] The control containing the criteria value.</param>
        /// <param name="PriceOption">[ComboBox] The control indicating how and whether the price should be read.</param>
        /// <param name="PriceToValidate">[double] The AMSServiceRef.Asset price being checked.</param>
        /// <param name="FromPrice">[bool] True if checking the from price field, otherwise false.  Required for Price Between.</param>
        /// <returns>[bool] True if the PriceToValidate meets the criteria, otherwise false.</returns>
        private bool IsValidPrice(TextBox PriceField, ComboBox PriceOption, double PriceToValidate, bool FromPrice)
        {
            double dbltest;

            if (Double.TryParse(PriceField.Text, out dbltest))
            {
                switch (PriceOption.Text)
                {
                    case PRICE_IS:
                        if (FromPrice)
                            return (Math.Abs(dbltest - PriceToValidate) < 0.000001);
                        break;
                    case PRICE_BEG:
                        if (FromPrice)
                            return (PriceToValidate >= dbltest);
                        break;
                    case PRICE_END:
                        if (!FromPrice)
                            return (PriceToValidate <= dbltest);
                        break;
                    case PRICE_BET:
                        if (FromPrice)
                            return (PriceToValidate >= dbltest);
                        else
                            return (PriceToValidate <= dbltest);
                        break;
                    case PRICE_ANY:
                        return true;
                        break;
                    default:
                        // An unexpected option was selected.
                        return false;
                        break;
                }
            }
            else
                return false;  // The price could not be converted to a double.

            return false;
        }
    }
}
